package com.lry.concurrent.locks;

public class ReentrantLock implements Lock {

    private final Sync sync;

    public ReentrantLock(){
        this.sync = new NoFairSync();
    }

    public Condition newCondition(){
        return sync.newCondition();
    }


    abstract static class Sync extends AbstractQueuedSynchronizer{

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        abstract void lock();

        @Override
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        @Override
        protected boolean tryRelease(int count) {
            if(getExclusiveOwnerThread()!=Thread.currentThread()){
                throw new IllegalMonitorStateException();
            }
            int c = getState() - count;
            //这里无竞争
            setState(c);
            if(c==0){
                setExclusiveOwnerThread(null);
                return true;
            }
            return false;
        }
    }

    static class NoFairSync extends Sync{

        @Override
        void lock() {
            //这里有竞争
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
            }else{
                acquire(1);
            }
        }

        @Override
        protected boolean tryAcquire(int count) {
            int c = getState();
            if(c==0){
                //这里有竞争
                if(compareAndSetState(0,count)){
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                }
            } else if(getExclusiveOwnerThread()==Thread.currentThread()){
                int nextc = getState() + count;
                if(nextc<0){
                    throw new IllegalStateException();
                }
                //这里无竞争
                setState(nextc);
                return true;
            }
            return false;
        }
    }



    @Override
    public void lock() {
        sync.lock();
    }

    @Override
    public void unlock() {
        sync.release(1);
    }
}
